
#ifndef XDNS_H
#define XDNS_H

#include <netinet/in.h>
#include <sys/select.h>
#include <string>
#include <vector>

enum XdnsError
{
    XE_Ok = 0,
    XE_BadResponse,             /*!< Bad response. */
    XE_Inval,                   /*!< Invalid argument. */
    XE_Network,                 /*!< Generic network error. */
    XE_NoEnt,                   /*!< No such entry. */
    XE_NoMem,                   /*!< Out of memory. */
    XE_NoSupport,               /*!< Not supported. */
    XE_Range,                   /*!< Size out of range. */
    XE_Unexpected,              /*!< An unexpected case occurred. */

    // DNS special errors.
    XE_UdpTruncated = 100,      /*!< A UDP reply is truncated. */
    XE_RcodeFormatError,        /*!< The name server was unable to
                                     interpret the query.
                                */
    XE_RcodeServerFailure,      /*!< The name server was unable to
                                     process this query due to a
                                     problem with the name server.
                                */
    XE_RcodeNameError,          /*!< Meaningful only for responses
                                     from an authoritative name server,
                                     this code signifies that the
                                     domain name referenced in the
                                     query does not exist.
                                */
    XE_RcodeNotImplemented,     /*!< The name server does not support
                                     the requested kind of query.
                                */
    XE_RcodeRefused             /*!< The name server refuses to perform
                                     the specified operation for policy
                                     reasons.
                                */
};

class XdnsServerConfig
{
public:
    /*! XdnsServerConfig default constructor. */
    XdnsServerConfig();

    /*! XdnsServerConfig constructor.

      @param serverPort DNS server port for both TCP and UDP. It should
                        follow the host byte order.
      @param serverIpAddress A string of DNS server IP address.
    */
    XdnsServerConfig(unsigned short serverPort,
                     const std::string& serverIpAddress);

    XdnsServerConfig(const XdnsServerConfig& anotherServerConfig);
    ~XdnsServerConfig();
    XdnsServerConfig& operator=(const XdnsServerConfig& anotherServerConfig);

    unsigned short m_serverPort;
    std::string m_serverIpAddress;
};

class XdnsConfig
{
public:
    /*! XdnsConfig default constructor. */
    XdnsConfig();

    /*! XdnsConfig constructor.

        This function is used to specify the custom DNS servers in the
        configuration. These configured DNS servers have the highe
        priority than the initialized and default DNS servers
        (For example, resolv.conf). So user should not specify any custom
        DNS servers if unnecessary.

        @param customServers Vector of the custom DNS servers.
    */
    XdnsConfig(const std::vector<XdnsServerConfig>& customServers);

    XdnsConfig(const XdnsConfig& anotherConfig);
    ~XdnsConfig();
    XdnsConfig& operator=(const XdnsConfig& anotherConfig);

    std::vector<XdnsServerConfig> m_customServers;
};

/*! The host entry is decoded from the DNS response. */
class XdnsHostEntry
{
public:
    XdnsHostEntry();
    virtual ~XdnsHostEntry();

    /*! Retrieve the official hostname.

        @return String of the hostname.
    */
    virtual std::string GetHostname() const = 0;

    /*! Retrieve the internet host address.

        @return Internet host address.
    */
    virtual const struct in_addr* GetAddress() const = 0;
};

class XdnsListener
{
public:
    XdnsListener();
    virtual ~XdnsListener();

    /*! Callback function for requesting the host.

        This callback function is called when user calls the functions
        GetHostByName or GetHostByAddress of class XdnsHandler.

        @param queryId Query ID identified which query is requested, the
                       query ID is returned when the requesting function is
                       called.
        @param error XE_Ok if success.
        @param hostEntry On return the host entries which are decoded from
                         the DNS response if success, NULL otherwise.
        @return None.
    */
    virtual void GetHostCallback(long queryId,
                                 XdnsError error,
                                 const std::vector<XdnsHostEntry*>* hostEntries
                                 ) = 0; 
};

class XdnsHandler
{
public:
    XdnsHandler();
    virtual ~XdnsHandler();

    /*! Initialize a new XDNS handler.

        @param config XDNS configuration.
        @param handler On return the new XDNS handler, it must be destroyed by
                       calling the function Release when it is useless.
        @return XE_Ok if success.
    */
    static XdnsError Init(const XdnsConfig& config,
                          XdnsHandler** handler);

    static void Release(XdnsHandler* handler);

    /*! Request the host by a name.

        This function is the asynchronous implementation of the system function
        gethostbyname.

        @param name Hostname used to request.
        @param listener The function GetHostCallback of this listener is called
                        when requesting is complete. It could be NULL if user
                        did not care about the requesting result.
        @param queryId On return the query ID if success. It could be NULL if
                       user did not care about it.
        @return XE_Ok if success.
    */
    virtual XdnsError GetHostByName(const std::string& name,
                                    XdnsListener* listener,
                                    long* queryId
                                    ) = 0;

    /*! Request the host by an address.

        This function is the asynchronous implementation of the system function
        gethostbyaddr.

        @param ipAddress IP address used to request.
        @param listener The function GetHostCallback of this listener is called
                        when requesting is complete. It could be NULL if user
                        did not care about the requesting result.
        @param queryId On return the query ID if success. It could be NULL if
                       user did not care about it.
        @return XE_Ok if success.
    */
    virtual XdnsError GetHostByAddress(const struct in_addr* ipAddress,
                                       XdnsListener* listener,
                                       long* queryId
                                       ) = 0;

    /*! Cancel query by a query ID.

        @param queryId Query ID identified which query should be canceled. 
        @return None.
    */
    virtual void CancelQuery(long queryId) = 0;

    /*! Retrieve the processing set of file descriptors to select.

        @param nfds On return One greater than the number of the highest socket
                    set in either readfds or writefds.
        @param readfds On return reading file descriptors.
        @param writefds On return writing file descriptors.
        @return XE_Ok if success,
                XE_NoEnt if no file descriptors is being processed.
    */
    virtual XdnsError GetProcessingFds(int* nfds,
                                       fd_set* readfds,
                                       fd_set* writefds
                                       ) = 0;

    /*! Process the activated file descriptors.

        This function should be called after calling the function select on the
        processing file descriptors, they are retrieved from the function
        GetProcessingFds.

        @param readfds Reading file descriptors used to process.
        @param writefds Writing file descriptors used to process.
        @return XE_Ok if success.
    */
    virtual XdnsError Process(fd_set* readfds,
                              fd_set* writefds
                              ) = 0;
};

#endif  // XDNS_H

